﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using DiscoveryLogic.Analytics.DataObjects.Networks;
using DiscoveryLogic.Common.Utilities;

namespace DiscoveryLogic.Analytics.DataObjects.Measures
{
    public class NetworkMeasure
    {
        private string _NetworkName;
        /// <summary></summary>
        public string NetworkName
        {
            get
            {
                return this._NetworkName;
            }
            set
            {
                this._NetworkName = value;
            }
        }

        private double _EdgeDiameter;
        /// <summary>
        /// maximum distance to walk from one node to 
        /// another along edges
        /// </summary>
        public double EdgeDiameter
        {
            get
            {
                return this._EdgeDiameter;
            }
            set
            {
                this._EdgeDiameter = value;
            }
        }

        private int _NodeDiameter;
        /// <summary>
        /// maximum number of hops to move 
        /// between most distant nodes in network
        /// </summary>
        public int NodeDiameter
        {
            get
            {
                return this._NodeDiameter;
            }
            set
            {
                this._NodeDiameter = value;
            }
        }

        private double _Density;
        /// <summary>
        /// # of links / max # of links
        /// </summary>
        public double Density
        {
            get
            {
                return this._Density;
            }
            set
            {
                this._Density = value;
            }
        }

        private double _InDensity;
        /// <summary></summary>
        public double InDensity
        {
            get
            {
                return this._InDensity;
            }
            set
            {
                this._InDensity = value;
            }
        }

        private double _OutDensitity;
        /// <summary></summary>
        public double OutDensitity
        {
            get
            {
                return this._OutDensitity;
            }
            set
            {
                this._OutDensitity = value;
            }
        }

        private double _DensitySD;
        /// <summary></summary>
        public double DensitySD
        {
            get
            {
                return this._DensitySD;
            }
            set
            {
                this._DensitySD = value;
            }
        }

        private double _InDensitySD;
        /// <summary></summary>
        public double InDensitySD
        {
            get
            {
                return this._InDensitySD;
            }
            set
            {
                this._InDensitySD = value;
            }
        }

        private double _OutDensitySD;
        /// <summary></summary>
        public double OutDensitySD
        {
            get
            {
                return this._OutDensitySD;
            }
            set
            {
                this._OutDensitySD = value;
            }
        }

        private Dictionary<EntityBase,Dictionary<EntityBase,NetworkRoute>> _Walks;
        /// <summary></summary>
        public Dictionary<EntityBase,Dictionary<EntityBase,NetworkRoute>> Walks
        {
            get
            {
                return this._Walks;
            }
            set
            {
                this._Walks = value;
            }
        }

        private List<CommunityMeasure> _CommunityMeasures;
        /// <summary></summary>
        public List<CommunityMeasure> CommunityMeasures
        {
            get
            {
                return this._CommunityMeasures;
            }
            set
            {
                this._CommunityMeasures = value;
            }
        }

        public NetworkMeasure()
        {
            this._NetworkName = string.Empty;
            this._CommunityMeasures=new List<CommunityMeasure>();
            this._Density = 0;
            this._EdgeDiameter = 0;
            this._NodeDiameter = 0;
            this._Walks=new Dictionary<EntityBase, Dictionary<EntityBase, NetworkRoute>>();
        }

        public DataTable ToWalkTable()
        {
            Network network = Network.Read(this._NetworkName);
            Dictionary<EntityType, Dictionary<string, EntityBase>> allEntities = network.GetEntities();
            DataTable dt=new DataTable();
            dt.Columns.Add("FromEntity", typeof (string));
            dt.Columns.Add("ToEntity", typeof (string));
            dt.Columns.Add("Distance", typeof (double));
            dt.Columns.Add("Hops", typeof (int));
            dt.Columns.Add("Path", typeof (string));

            if(this.Walks !=null && this.Walks.Count>0)
            {
                foreach(EntityBase fromEntity in this.Walks.Keys)
                {
                    foreach(EntityBase toEntity in this.Walks[fromEntity].Keys)
                    {
                        if(fromEntity==toEntity)
                            continue;

                        DataRow dr = dt.NewRow();
                        dr["FromEntity"] = fromEntity.Name;
                        dr["ToEntity"] = toEntity.Name;
                        NetworkRoute route = this.Walks[fromEntity][toEntity];
                        dr["Distance"] = (double.IsInfinity(route.Distance) || double.IsNaN(route.Distance))
                                             ? -1
                                             : route.Distance;
                        dr["Hops"] = route.Hops;
                        string path = string.Format("{0}:{1}", fromEntity.EntityType.ToString(), fromEntity.Name);
                        foreach(Link link in route.Path)
                        {
                            path += " -> ";
                            EntityBase nextEntity = allEntities[EntityTypeUtil.GetEntityType(link.ToType)][link.ToKey];
                            path += string.Format("{0}:{1}", nextEntity.EntityType.ToString(), nextEntity.Name);
                        }
                        dr["Path"] = path;
                        dt.Rows.Add(dr);
                    }
                }
            }
            return dt;
        }

        public DataTable ToCommunityTable()
        {
            DataTable dt=new DataTable();
            dt.Columns.Add("CommunityID", typeof (int));
            dt.Columns.Add("EntityCount", typeof (int));
            dt.Columns.Add("Density", typeof (double));
            dt.Columns.Add("Degree", typeof (int));
            dt.Columns.Add("InDegree", typeof(int));
            dt.Columns.Add("OutDegree", typeof(int));
            dt.Columns.Add("Coefficient", typeof (double));
            if(this.CommunityMeasures !=null && this.CommunityMeasures.Count>0)
            {
                foreach(CommunityMeasure commMeasure in this.CommunityMeasures)
                {
                    DataRow dr = dt.NewRow();
                    dr["CommunityID"] = commMeasure.CommunityID;
                    dr["EntityCount"] = commMeasure.Entities.Count;
                    dr["Density"] = (double.IsInfinity(commMeasure.Density) || double.IsNaN(commMeasure.Density))
                                        ? -1
                                        : commMeasure.Density;
                    dr["Degree"] = commMeasure.Degree;
                    dr["InDegree"] = commMeasure.InDegree;
                    dr["OutDegree"] = commMeasure.OutDegree;
                    dr["Coefficient"] = (double.IsInfinity(commMeasure.Coefficient) ||
                                         double.IsNaN(commMeasure.Coefficient))
                                            ? -1
                                            : commMeasure.Coefficient;
                    dt.Rows.Add(dr);
                }
            }
            return dt;
        }
    }
}
